package cn.edu.fzu.homemaking.chat.socket;

import cn.edu.fzu.homemaking.chat.cache.CompanyCache;
import cn.edu.fzu.homemaking.chat.cache.UserCache;
import cn.edu.fzu.homemaking.chat.config.ChatConfig;
import cn.edu.fzu.homemaking.chat.domain.dto.MessageDto;
import cn.edu.fzu.homemaking.chat.domain.entity.Message;
import cn.edu.fzu.homemaking.chat.domain.ko.DoubleLongKey;
import cn.edu.fzu.homemaking.chat.service.MessageService;
import cn.edu.fzu.homemaking.common.SessionConstant;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/chat/{userType}/{fromId}/{toId}")
@Component
public class WebSocketServer {

    private static final Logger                            log                = LoggerFactory
            .getLogger(WebSocketServer.class);

    private static final String                            WELCOME            = "您好，很高兴为您服务，请问有什么能帮助到您的吗？";

    private static final String                            COM_OFFLINE        = "当前商家不在线，消息可能回复不及时";
    //记录当前在线连接数
    private static final Map<Long, Session>                userMap            = new ConcurrentHashMap<>();

    //记录当前在线连接数
    private static final ListMultimap<Long, Session>       companyMap         = ArrayListMultimap.create();

    //客服等待集合<客服id，顾客id>
    private static final ListMultimap<Session, Long>       comWaitList        = ArrayListMultimap.create();

    //顾客-客服 对应关系 <顾客，客服>
    private static final Map<DoubleLongKey, Session>       customerSupportMap = new HashMap<>();

    private static final ListMultimap<Session, MessageDto> messageCache       = ArrayListMultimap.create();

    private static UserCache                               userCache;

    private static MessageService                          messageService;

    private static CompanyCache                            companyCache;

    private static RabbitTemplate                          rabbitTemplate;

    @Autowired
    public void setUserCache(UserCache userCache) {
        WebSocketServer.userCache = userCache;
    }


    @Autowired
    public void setMessageService(MessageService messageService) {
        WebSocketServer.messageService = messageService;
    }


    @Autowired
    public void setCompanyCache(CompanyCache companyCache) {
        WebSocketServer.companyCache = companyCache;
    }


    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        WebSocketServer.rabbitTemplate = rabbitTemplate;
    }


    /** 连接建立成功调用的方法 **/
    @OnOpen
    public void onOpen(Session session, @PathParam("userType") String userType, @PathParam("fromId") Long fromId,
                       @PathParam("toId") Long toId) {
        //发送历史消息
        sendHistory(session, fromId, toId, userType);
        if (SessionConstant.USER.equals(userType)) {
            userMap.put(fromId, session);
            //给顾客分配客服
            allotCompanyUser(session, fromId, toId);
        } else if (SessionConstant.COMPANY.equals(userType)) {
            companyMap.put(fromId, session);
        }
        messageService.updateRead(fromId, toId, userType);
    }


    /** 连接关闭调用的方法 **/
    @OnClose
    @OnError
    public void onClose(Session session, @PathParam("userType") String userType, @PathParam("fromId") Long fromId,
                        @PathParam("toId") Long toId) {
        if (SessionConstant.USER.equals(userType)) {
            userMap.remove(fromId, session);
            customerSupportMap.remove(DoubleLongKey.create(fromId, toId));
        } else if (SessionConstant.COMPANY.equals(userType)) {
            companyMap.remove(fromId, session);
            comWaitList.remove(session, toId);
        }
        //将消息保存到数据库
        saveMessage(messageCache.get(session));
    }


    /**
     * 收到客户端消息后调用的方法 后台收到客户端发送过来的消息 onMessage 是一个消息的中转站 接受 浏览器端 socket.send 发送过来的
     * json数据
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("userType") String userType,
                          @PathParam("fromId") Long fromId, @PathParam("toId") Long toId) {
        MessageDto msg = JSON.parseObject(message, MessageDto.class);
        MessageDto messageDto = new MessageDto();
        //用户发消息
        if (SessionConstant.USER.equals(userType)) {
            if (msg.isHuman()) {
                messageDto.setFromId(fromId);
                messageDto.setFromName(userCache.getName(fromId, SessionConstant.USER));
                messageDto.setToId(toId);
                messageDto.setText(msg.getText());
                messageDto.setCreateTime(new Date());
                messageDto.setRead(true);
                messageDto.setOrigin(userType);

                //放入消息缓存
                messageCache.put(session, messageDto);

                //查询出为该顾客服务的客服
                Session call = customerSupportMap.get(DoubleLongKey.create(fromId, toId));
                if (call != null) {
                    //发消息给客服
                    sendMessage(messageDto, call);
                }

            } else {
                //机器人自动消息
                String robotMsg = companyCache.getRobotMsg(toId, msg.getText());
                messageDto.setFromId(toId);
                messageDto.setFromName(companyCache.getNameById(toId));
                messageDto.setToId(fromId);
                messageDto.setText(robotMsg);
                messageDto.setCreateTime(new Date());
                messageDto.setOrigin(SessionConstant.COMPANY);
                sendMessage(messageDto, session);
            }
        }
        //公司客服发消息
        else {
            messageDto.setFromId(fromId);
            messageDto.setFromName(companyCache.getNameById(fromId));
            messageDto.setToId(toId);
            messageDto.setText(msg.getText());
            messageDto.setOrigin(userType);
            messageDto.setCreateTime(new Date());

            Session user = userMap.get(toId);
            if (user != null) {
                messageDto.setRead(true);
                sendMessage(messageDto, user);
            }
            //放入消息缓存
            messageCache.put(session, messageDto);
        }

    }


    /** 服务端发送消息给客户端 **/
    private void sendMessage(MessageDto message, Session toSession) {
        try {
            log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(JSON.toJSONString(message));
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }


    /** 给顾客分配客服 **/
    private void allotCompanyUser(Session session, Long fromId, Long toId) {
        MessageDto message = new MessageDto();
        message.setFromId(toId);
        message.setFromName(companyCache.getNameById(toId));
        message.setOrigin(SessionConstant.COMPANY);
        message.setToId(fromId);
        message.setCreateTime(new Date());

        //给顾客分配一个客服
        List<Session> sessions = companyMap.get(toId);
        if (CollectionUtils.isEmpty(sessions)) {
            message.setText(COM_OFFLINE);
            message.setTip(true);
            sendMessage(message, session);
            return;
        }

        //选择等待人数最少的客服
        int minWait = Integer.MIN_VALUE;
        Session candidate = null;
        for (Session se : sessions) {
            List<Long> wait = comWaitList.get(se);
            if (wait.size() == 0) {
                candidate = se;
                break;
            }
            if (wait.size() < minWait) {
                minWait = wait.size();
                candidate = se;
            }
        }
        //在该顾客对应的该商家绑定一个客服
        customerSupportMap.put(DoubleLongKey.create(fromId, toId), candidate);
        //在客服等待列表添加该顾客
        comWaitList.put(candidate, fromId);
        //顾客咨询，发送欢迎提示
        message.setText(WELCOME);
        sendMessage(message, session);
    }


    /** 发送历史信息 **/
    private void sendHistory(Session session, Long fromId, Long toId, String userType) {

        //查询接受到对方的消息
        List<Message> receive = messageService.listReceive(fromId, toId, userType);

        //查询发送到对方的消息
        List<Message> send = messageService.listReceive(toId, fromId,
                SessionConstant.USER.equals(userType) ? SessionConstant.COMPANY : SessionConstant.USER);

        //两者消息整合
        receive.addAll(send);

        if (CollectionUtils.isEmpty(receive)) {
            return;
        }

        //按消息时间排序
        receive.sort(new Comparator<Message>() {
            @Override
            public int compare(Message o1, Message o2) {
                return o1.getCreateTime().compareTo(o2.getCreateTime());
            }
        });
        for (Message message : receive) {
            MessageDto dto = new MessageDto();
            dto.setFromId(message.getFrom());
            dto.setToId(message.getTo());
            dto.setText(message.getMsg());
            dto.setCreateTime(message.getCreateTime());
            dto.setOrigin(message.getOrigin());
            sendMessage(dto, session);
        }
    }


    //将消息保存到mq中消费
    public void saveMessage(List<MessageDto> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (MessageDto dto : list) {
            rabbitTemplate.convertAndSend(ChatConfig.EXCHANGE_TOPICS_CHAT, "inform.chat.message", dto);
        }
    }
}
